package com.lxm.service.impl;

import cn.hutool.core.util.BooleanUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.baomidou.mybatisplus.extension.toolkit.Db;
import com.lxm.common.contants.LikesConstants;
import com.lxm.common.contants.RedisExpire;
import com.lxm.common.tools.UserHolder;
import com.lxm.dto.Result;
import com.lxm.entity.Blog;
import com.lxm.entity.LxmComment;
import com.lxm.entity.Item;
import com.lxm.entity.Likes;
import com.lxm.mapper.LikesDao;
import com.lxm.service.LikesService;
import jakarta.annotation.Resource;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.function.BiFunction;

/**
 * <p>
 * 点赞表 服务实现类
 * </p>
 *
 * @since 2024-10-02
 */
@Service
public class LikesServiceImpl extends ServiceImpl<LikesDao, Likes> implements LikesService {
    // 点赞函数映射关系
    private static final Map<Integer, BiFunction<Long, Integer, Boolean>> updateMethods
            = new HashMap<>();
    @Resource
    private StringRedisTemplate stringRedisTemplate;

    public LikesServiceImpl() {
        // 初始化映射关系
        updateMethods.put(LikesConstants.BLOG_LIKE, (id, v)
                -> updateLikeNum(Blog.class, "blog_id", id, v));
        updateMethods.put(LikesConstants.COMMENT_LIKE, (id, v)
                -> updateLikeNum(LxmComment.class, "comment_id", id, v));
        updateMethods.put(LikesConstants.ITEM_LIKE, (id, v)
                -> updateLikeNum(Item.class, "item_id", id, v));
    }

    @Override
    public Boolean checkLike(Long likedId, Integer type) {
        Long userId = UserHolder.getUser().getUserId();
        // 未登录检测
        if (userId == null) {
            return false;
        }
        // 先检查redis
        Boolean isLikeRedis = stringRedisTemplate
                .opsForSet().isMember(getRedisPrefix(type) + userId,
                        likedId.toString());
        if (BooleanUtil.isTrue(isLikeRedis)) {
            return isLikeRedis;
        }
        //redis检查完后，进行SQL复查
        boolean isLike = exists(new LambdaQueryWrapper<Likes>()
                .eq(Likes::getUserId, userId)
                .eq(Likes::getLikedId, likedId)
                .eq(Likes::getType, type));
        return BooleanUtil.isTrue(isLike);
    }


    @Override
    public Result like(Long likedId, Integer type) {
        Long userId = UserHolder.getUser().getUserId();
        Likes like = new Likes();
        like.setUserId(userId);
        like.setLikedId(likedId);
        like.setType(type);
        boolean isSave = save(like);
        // 如果点赞失败，则返回失败
        if (!isSave) return Result.fail(LikesConstants.LIKE_FAIL);
        // 更新点赞数
        Boolean isLike = updateMethods.get(type).apply(likedId, 1);
        if (!isLike) return Result.fail(LikesConstants.LIKE_FAIL);
        // 将点赞信息存入redis
        stringRedisTemplate.opsForSet()
                .add(getRedisPrefix(type) + userId, likedId.toString());
        // 设置过期时间
        stringRedisTemplate.expire(getRedisPrefix(type) + userId,
                RedisExpire.LIKE_EXPIRE, TimeUnit.DAYS);
        return Result.ok(LikesConstants.LIKE_SUCCESS);
    }


    @Override
    public Result cancelLike(@NotNull Long likeId, @NotNull Integer type) {
        Long userId = UserHolder.getUser().getUserId();
        stringRedisTemplate.opsForSet()
                .remove(getRedisPrefix(type) + userId, likeId.toString());
        boolean isRemove = remove(new LambdaQueryWrapper<Likes>()
                .eq(Likes::getLikedId, likeId)
                .eq(Likes::getUserId, userId));
        if (!isRemove) return Result.fail(LikesConstants.CANCEL_LIKE_FAIL);
        //如果点赞表没有记录，即便当like_num时，前端发过来减的请求，依然不会影响其他三个的点赞数
        Boolean isCancelLike = updateMethods.get(type).apply(likeId, -1);
        if (!isCancelLike) return Result.fail(LikesConstants.CANCEL_LIKE_FAIL);
        return Result.ok(LikesConstants.CANCEL_LIKE_SUCCESS);
    }

    @Nullable
    @Contract(pure = true)
    private String getRedisPrefix(@NotNull Integer type) {
        return switch (type) {
            case 1 -> LikesConstants.LIKE_BLOG_KEY;
            case 2 -> LikesConstants.LIKE_COMMENT_KEY;
            case 3 -> LikesConstants.LIKE_ITEM_KEY;
            default -> null;
        };
    }

    private <T> Boolean updateLikeNum(Class<T> clazz, String id,
                                      Long likedId, Integer num) {
        // 转换为 SFunction 类型
        boolean update = Db.update(clazz)
                .eq(id, likedId)
                .setSql("like_num = like_num +" + num)
                .update();
        return update;
    }
}